/*
    lisp_serialization.js

    Serialization of javascript data structures to lisp data.

*/


var m = {
	'\b': '\\b',
	'\t': '\\t',
	'\n': '\\n',
	'\f': '\\f',
	'\r': '\\r',
	'"' : '\\"',
	'\\': '\\\\'
};


function Serializer() {
	this._objectBeforeAdvice = [];
}

Serializer.prototype  = {
	// Unserialize
	read: function (str) {
		throw new Error("Not implemented.");
	},
	// Serialize
	print: function(obj) {
		if (obj instanceof Array) {
			return this.array(obj);
		} else {
			return this.object(obj);
		}
	},
	addObjectBeforeAdvice: function(advice) {
		if (!( advice in this._objectBeforeAdvice)) {
			this._objectBeforeAdvice.push(advice);
		}
	},
	// Internal stuff
	array: function (x) {
		var a = ['['], b, f, i, l = x.length, v;
		for (i = 0; i < l; i += 1) {
			v = x[i];
			f = this[typeof v];
			if (f) {
				v = f.call(this, v);
				if (typeof v == 'string') {
					if (b) {
						a[a.length] = ' ';
					}
					a[a.length] = v;
					b = true;
				}
			}
		}
		a[a.length] = ']';
		return a.join('');
	},
	'boolean': function (x) {
		return ((x) ? "t" : ":json-false");
	},
	'null': function (x) {
		return "nil";
	},
	number: function (x) {
		return isFinite(x) ? String(x) : 'nil';
	},
	"function": function (x) {
		throw new Error("JSON: Cannot serialize functions.");
	},
	object: function (x) {
		if (x != null) {
			if (x instanceof Array) {
				return this.array(x);
			}
			x = this._runAdvice(x, this._objectBeforeAdvice);
			var a = ['('], f, i, v;
			for (i in x) {
				if (x.hasOwnProperty(i)) {
					v = x[i];
					f = this[typeof v];
					if (f) {
						v = f.call(this, v);
						if (typeof v == 'string') {
							var key = this.string(i);
							key = key.substring(1, key.length - 1);
							a.push("(", key, ' . ', v, ')');
						}
					}
				}
			}
			a[a.length] = ')';
			return a.join('');
		} else {
			return this["null"]();
		}
	},
	string: function (x) {
		if (escape_regexp.test(x)) {
			var f1 = function(a, b) {
				var c = m[b];
				if (c) {
					return c;
				}
				c = b.charCodeAt();
				return '\\u00' +
				Math.floor(c / 16).toString(16) +
				(c % 16).toString(16);
			};
			x = x.replace(escape2_regexp, f1);
		};
		return '"' + x + '"';
	},
	_runAdvice: function (obj, adviceList) {
		var result = obj;
		for (var i = 0; i < adviceList.length; ++i) {
			result = adviceList[i](result);
		}
		return result;
	}

};

// These regexp mess up syntax parsing in ecmascript mode in emacs, so they are at the end
var escape_regexp = /["\\\x00-\x1f]/;
var escape2_regexp = /([\x00-\x1f\\"])/g;
var escape3_regexp = /[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/;
var escape4_regexp = /"(\\.|[^"\\])*"/g;
